#include "Camera.h"
#include "ui_Camera.h"
#include <QDebug>
#include <thread>
#include <QSettings>
#include <QCameraViewfinder>
#include <QFileDialog>

#define WIDTH 1280
#define HEIGHT 740

#define SETTINGS_FILE_PATH "camera_settings.ini"

Camera::Camera(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Camera)
{
    ui->setupUi(this);

    msgBox_err.setWindowTitle("Error");
    msgBox_err.resize(400, 200);
    msgBox_err.setIcon(QMessageBox::Critical);
    msgBox_inf.setWindowTitle("Info");
    msgBox_inf.resize(400, 200);
    msgBox_inf.setIcon(QMessageBox::Information);

    /*外部配置项*/
    int pullFlowWay;                    //拉流方式
    std::string rtpUrl;                 //拉流链接
    std::string savingPath;             //录像保存路径
    int scalingIndex;                     //显示窗口缩放比例
    settingsFilePath = (QString("%1/%2").arg(QApplication::applicationDirPath()).arg(SETTINGS_FILE_PATH));
    if(QFile::exists(settingsFilePath)){
        QSettings settings(settingsFilePath, QSettings::IniFormat);
        settings.beginGroup("CAMERASettings");
        pullFlowWay = settings.value("PULLWAY").toInt();
        scalingIndex = settings.value("SCALING").toInt();
        rtpUrl = settings.value("RTPURL").toString().toStdString();
        savingPath = settings.value("SAVINGPATH").toString().toStdString();
        settings.endGroup();
    }
    else{
        pullFlowWay = 1;
        scalingIndex = 1;
        rtpUrl = "rtsp://admin:hbw123456@192.168.1.64:554/Streaming/channels/101";
        savingPath = QApplication::applicationDirPath().toStdString();
    }
    this->cachePath = QCoreApplication::applicationDirPath().toStdString();
    qDebug() << "cachePath = " << QCoreApplication::applicationDirPath();

    this->ui->comboBox_way->setCurrentIndex(pullFlowWay);
    this->ui->comboBox_scaling->setCurrentIndex(scalingIndex);
    this->ui->lineEdit_Url->setText(QString::fromStdString(rtpUrl));
    this->ui->lineEdit_SavingPath->setText(QString::fromStdString(savingPath));

    emit on_comboBox_way_activated(ui->comboBox_way->currentText());
    connect(this, &Camera::signal_videoBegin, this, &Camera::slot_videoBegin);
    connect(this, &Camera::signal_videoEnd, this, &Camera::slot_videoStop);
    connect(this, &Camera::signal_cameraOpen, this, &Camera::slot_cameraOpen);
    connect(this, &Camera::signal_cameraReady, this, &Camera::slot_cameraReady);

    return;
}

Camera::~Camera()
{
    delete ui;
}

void Camera::populateCameraList()
{
    m_cameras = QCameraInfo::availableCameras();
    ui->comboBox_availableDevice->clear();

    //If no availableCamera then setting a default item
    if (m_cameras.isEmpty())
    {
        ui->comboBox_availableDevice->addItem("No available camera");
        return;
    }

    for (const QCameraInfo &camera : m_cameras)
    {
        ui->comboBox_availableDevice->addItem(camera.description());
    }

    m_camera_info = m_cameras.first();
    //getResolution(m_camera_info);
}

int Camera::videoCaptureInit()
{
    switch (this->ui->comboBox_way->currentText().at(0).toLatin1()) {
    case 'U':
        qDebug() << "videoCaptureInit: Url Init";
        this->capture = cv::VideoCapture(this->ui->lineEdit_Url->text().toStdString());
        break;
    case 'L':
        qDebug() << "videoCaptureInit: Local Init";
        if (ui->comboBox_availableDevice->currentText() == "No available camera")
        {
            msgBox_err.setText("No available camera");
            msgBox_err.exec();
            return -1;
        }
        this->capture = cv::VideoCapture(ui->comboBox_availableDevice->currentIndex());
        break;
    }

    if (!this->capture.isOpened())
    {
        msgBox_err.setText("Camera open failed");
        msgBox_err.exec();
        return -1;
    }

    int width = (int)capture.get(cv::CAP_PROP_FRAME_WIDTH);
    int height = (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT);

    if (width < 0 || height < 0)
    {
        capture.set(cv::CAP_PROP_FRAME_WIDTH, WIDTH);
        capture.set(cv::CAP_PROP_FRAME_HEIGHT, HEIGHT);
    }

    return 0;
}

void Camera::on_comboBox_way_activated(const QString &arg1)
{
    switch (arg1.at(0).toLatin1()) {
    case 'U':
        ui->comboBox_availableDevice->setEnabled(false);
        ui->lineEdit_Url->setEnabled(true);
        break;
    case 'L':
        populateCameraList();
        ui->comboBox_availableDevice->setEnabled(true);
        ui->lineEdit_Url->setEnabled(false);
        break;
    }
}

void Camera::on_pushButton_test_clicked()
{
    if(videoCaptureInit() < 0)
    {
        return;
    }

    //测试过程中启用录像
    emit signal_videoBegin();

    //判断
    if (isEnterDesktop(capture))
    {
        //true
        msgBox_inf.setText("Test Success");
    }
    else
    {
        //false
        msgBox_inf.setText("Test Failed");
    }
    msgBox_inf.exec();

    //录像结束
    emit signal_videoEnd();

    cv::destroyAllWindows();
    return;
}

bool Camera::colorRecog(cv::Mat img, cv::Scalar lower, cv::Scalar upper)
{
    int count_all = 0, count_white = 0;
    cv::Mat hsv, mask;
    cvtColor(img, hsv, cv::COLOR_BGR2HSV);
    cv::inRange(hsv, lower, upper, mask);

    //cv::imshow("frame", img);
    //cv::imshow("hsv", hsv);
    cv::imshow("mask", mask);

    cv::Mat dst = cv::Mat(mask.size(), CV_8UC3);
    for (int i = 0; i < mask.rows; i++)
    {
        for (int j = 0; j < mask.cols; j++)
        {
            if (mask.channels() == 1)
            {
                count_all++;
                if (mask.at<uchar>(i, j) == 255)
                    count_white++;
            }
        }
    }

    if ((float)count_white / count_all > 0.6f)
        return true;

    return false;
}

bool Camera::isEnterDesktop(cv::VideoCapture& capture)
{
    if (!capture.isOpened())
    {
        qDebug() << "isEnterDesktop: Failed to open camera";
        return false;
    }
    emit signal_cameraOpen();

    int step = 0;
    bool ret = false;
    int fps = (int)capture.get(cv::CAP_PROP_FPS);
    int width = (int)capture.get(cv::CAP_PROP_FRAME_WIDTH);
    int height = (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT);

    if (fps <= 0 || width <= 0 || height <= 0)
    {
        fps = 30;
        width = WIDTH;
        height = HEIGHT;
    }

    width = static_cast<int>(width * ui->comboBox_scaling->currentText().toDouble());
    height = static_cast<int>(height * ui->comboBox_scaling->currentText().toDouble());

    #ifdef __linux__
        //videoWriter = cv::VideoWriter(cachePath + "/cache.mp4", cv::VideoWriter::fourcc('M', 'P', '4', 'V'), fps, cv::Size(width, height));
        videoWriter = cv::VideoWriter(cachePath + "/cache.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, cv::Size(width, height));
    #elif defined(_WIN32)
        videoWriter = cv::VideoWriter(cachePath + "/cache.avi", cv::VideoWriter::fourcc('X', 'V', 'I', 'D'), fps, cv::Size(width, height));
    #else	//其它
        videoWriter = cv::VideoWriter(cachePath + "/cache.avi", cv::VideoWriter::fourcc('D', 'I', 'V', 'X'), fps, cv::Size(width, height));
    #endif

    // 检查VideoWriter是否成功打开
    if (!videoWriter.isOpened())
    {
        qDebug() << "isEnterDesktop: 无法打开视频写入器";
        //capture.release();
        return -1;
    }

    cv::Mat frame(height, width, CV_8UC3);
    cv::Mat frame_rcv(height, width, CV_8UC3);

    cv::Scalar lower_red(0, 43, 46);
    cv::Scalar upper_red(10, 255, 255);
    cv::Scalar lower_green(35, 43, 46);
    cv::Scalar upper_green(75, 255, 255);
    cv::Scalar lower_blue(100, 43, 46);
    cv::Scalar upper_blue(140, 255, 255);

    float coefficient = 0.3;
#if 0 //Debug
    while (1)
#else
    for(int i = 0;  i < 1000; ++i, SLEEP_FOR_MSECONDS(1))
#endif
    {
        //qDebug() << "i = " << i;
        capture.read(frame_rcv);
        cv::resize(frame_rcv, frame, cv::Size(width, height), 0, 0, cv::INTER_LINEAR);

        cv::Mat temp = this->drawRec(frame, coefficient);
        if(temp.empty())
        {
            qDebug() << "isEnterDesktop: Read Empty";
            continue;
        }
        cv::imshow("请将颜色模块对准方框", temp);

        if (videoStopTag)
        {
            videoWriter.write(frame); // 将帧写入视频文件
        }

        switch(step)
        {
        case 0:
            ret = colorRecog(this->cropImageCenter(frame, coefficient), lower_red, upper_red);
            break;
        case 1:
            ret = colorRecog(this->cropImageCenter(frame, coefficient), lower_green, upper_green);
            break;
        case 2:
            ret = colorRecog(this->cropImageCenter(frame, coefficient), lower_blue, upper_blue);
            break;
        }

        if (ret)
        {
            qDebug() << "Found Step = " << step;
            ++step;
            if(step > 2)
            {
                frame.release();
                emit signal_cameraReady();
                return true;
            }
        }

        if (cv::waitKey(1) == 'q')
        {
            break;
        }
    }

    qDebug() << "Not Found";
    frame_rcv.release();
    frame.release();
    emit signal_cameraReady();
    return false;
}

cv::Mat Camera::cropImageCenter(const cv::Mat& src, float coefficient = 1.0)
{
    if (src.empty())
    {
        qDebug() << "cropImageCenter: Input image is empty!";
        return cv::Mat();
    }

    int srcWidth = src.cols;
    int srcHeight = src.rows;

    int startX = (int)((srcWidth - (srcWidth * coefficient)) / 2);
    int startY = (int)((srcHeight - (srcHeight * coefficient)) / 2);

    cv::Rect cropRect(startX, startY, (int)(srcWidth * coefficient), (int)(srcHeight * coefficient));

    // 使用Rect对象截取图像
    cv::Mat croppedImage = src(cropRect);

    return croppedImage;
}

// 在原始图像上绘制截取区域的矩形
cv::Mat Camera::drawRec(const cv::Mat& src, float coefficient = 1.0)
{
    if (src.empty())
    {
        qDebug() << "drawRec: Input image is empty!";
        return cv::Mat();
    }
    cv::Mat dst = src;

    int srcWidth = src.cols;
    int srcHeight = src.rows;

    int startX = (int)((srcWidth - (srcWidth * coefficient)) / 2);
    int startY = (int)((srcHeight - (srcHeight * coefficient)) / 2);

    cv::rectangle(dst, cv::Point(startX, startY), cv::Point(startX + (int)(srcWidth * coefficient), startY + (int)(srcHeight * coefficient)), cv::Scalar(0, 255, 0), 2);

    return dst;
}

bool Camera::cacheDelete()
{
    QFile file(QString::fromStdString(cachePath) + "/cache.avi");
    //qDebug() << "FileName = " << file.fileName();
    if(!file.exists())
    {
        qDebug() << "cacheDelete: Cache video not exist!";
        return true;
    }

    if(!file.resize(0))
    {
        qDebug() << "cacheDelete: File cannot delete!";
        file.close();
        return false;
    }

    file.close();
    return true;
}

bool Camera::cacheSaving(const std::string filename)
{
    QFile file(QString::fromStdString(cachePath) + "/cache.avi");
    //qDebug() << "FileName = " << file.fileName();
    if(!file.exists())
    {
        qDebug() << "cacheSaving: Cache video not exist!";
        return false;
    }

    if(file.rename(QString::fromStdString(this->ui->lineEdit_SavingPath->text().toStdString() + "/" + filename)))
    {
        qDebug() << "cacheSaving: Rename success";
        return true;
    }else
    {
        if (QFile::copy(QString::fromStdString(cachePath) + "/cache.avi", QString::fromStdString(this->ui->lineEdit_SavingPath->text().toStdString() + "/" + filename)))
        {
            if(!file.resize(0))
            {
                qDebug() << "cacheSaving: File cannot cut!";
                file.close();
                return false;
            }
        }else
        {
            qDebug() << "cacheSaving: Cannot copy file!";
            file.close();
            return false;
        }
    }

    return true;
}

void Camera::slot_videoStop(void)
{
    this->videoStopTag = 0;
}

void Camera::slot_cameraOpen()
{
    ui->comboBox_way->setDisabled(true);
    ui->comboBox_scaling->setDisabled(true);
    ui->comboBox_availableDevice->setDisabled(true);
    ui->lineEdit_SavingPath->setDisabled(true);
}

void Camera::slot_cameraReady()
{
    ui->comboBox_way->setDisabled(false);
    ui->comboBox_scaling->setDisabled(false);
    ui->comboBox_availableDevice->setDisabled(false);
    ui->lineEdit_SavingPath->setDisabled(false);
}

void Camera::slot_videoBegin(void)
{
    this->videoStopTag = 1;
}

void Camera::on_pushButton_save_clicked()
{
    QSettings settings(settingsFilePath, QSettings::IniFormat);
    settings.beginGroup("CAMERASettings");
    settings.setValue("PULLWAY", this->ui->comboBox_way->currentIndex());
    settings.setValue("SCALING", this->ui->comboBox_scaling->currentIndex());
    settings.setValue("RTPURL", this->ui->lineEdit_Url->text());
    settings.setValue("SAVINGPATH", this->ui->lineEdit_SavingPath->text());
    settings.endGroup();
    qDebug() << "Saving to " << settingsFilePath;
}

void Camera::on_pushButton_select_clicked()
{
    QString directory = QFileDialog::getExistingDirectory(this, tr("Select Directory Please", ""));
    if(!directory.isEmpty()){
        ui->lineEdit_SavingPath->setText(directory);
    }
}
